#include "bsp.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/time.h>

#include "defines.h"

int bsp_init() {
    // 开启 MIPI 液晶屏背光
    system("cd /sys/class/gpio/; echo 5 > export; echo out > gpio5/direction; echo 1 > gpio5/value");
    usleep(200000);
    system("echo 0 > /sys/class/gpio/gpio5/value");
    usleep(200000);
    system("echo 1 > /sys/class/gpio/gpio5/value");
    usleep(20000);
    return 0;
}

void bsp_exit() {
    // 关闭 MIPI 液晶屏背光
    system("echo 0 > /sys/class/gpio/gpio5/value");
}

/*
 * 串口设置
 * Serial port settings
 */
static int uart_config(int fd) {
    struct termios newtio = {0}, oldtio = {0};
    /*
     * 获取原有串口配置
     * Get the original serial port configuration
     */
    if (tcgetattr(fd, &oldtio) != 0) {
        perror("SetupSerial 1");
        return -1;
    }
    (void) memset_s(&newtio, sizeof(newtio), 0, sizeof(newtio));
    /*
     * CREAD开启串行数据接收，CLOCAL打开本地连接模式
     * CREAD opens serial data reception, CLOCAL opens local connection mode
     */
    newtio.c_cflag |= CLOCAL | CREAD;
    /*
     * 设置数据位
     * Set data bit
     */
    newtio.c_cflag &= ~CSIZE;
    newtio.c_cflag |= CS8;
    /*
     * 设置奇偶校验位
     * Set parity bit
     */
    newtio.c_cflag &= ~PARENB; // 无奇偶校验
    /*
     * 设置波特率115200
     * Set baud rate 115200
     */
    cfsetispeed(&newtio, B115200);
    cfsetospeed(&newtio, B115200);
    /*
     * 设置停止位
     * Set stop bit
     */
    newtio.c_cflag &= ~CSTOPB; /* 默认为一位停止位 */
    /*
     * 设置最少字符和等待时间，对于接收字符和等待时间没有特别的要求时
     *
     * Set the minimum characters and waiting time,
     * when there are no special requirements for receiving characters and waiting time
     */
    newtio.c_cc[VTIME] = 0; /* 非规范模式读取时的超时时间 */
    newtio.c_cc[VMIN] = 0; /* 非规范模式读取时的最小字符数 */
    /*
     * tcflush清空终端未完成的输入/输出请求及数据；TCIFLUSH表示清空正收到的数据，且不读取出来
     *
     * tcflush clears the unfinished input/output requests and data of the terminal;
     * TCIFLUSH means clearing the data being received and not reading it out
     */
    tcflush(fd, TCIFLUSH);
    if ((tcsetattr(fd, TCSANOW, &newtio)) != 0) {
        perror("com set error");
        return -1;
    }
    return 0;
}

int uart_open(int num) {
    int fd;
    char uart[16];
    sprintf(uart, "/dev/ttyAMA%d", num);

    if ((fd = open(uart, O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
        printf("open %s is failed", uart);
        return -1;
    }

    if (uart_config(fd) < 0) {
        printf("config %s is failed", uart);
        close(fd);
        return -1;
    }

    return fd;
}

void uart_close(int fd) {
    close(fd);
}

/*
 * @berf uart 读取数据
 * @param int uart_fd: 串口文件描述符
 * @param void *buf: 读取数据buffer
 * @param int len: 数据缓冲区长度
 * @param int timeout: 读取数据时间(毫秒)
 *
 * @berf uart read
 * @param int uart_fd: uart file descriptor
 * @param void *buf: read data buf
 * @param int len: data buf len
 * @param int timeout: read data time(ms)
 */
int uart_read(int fd, char *buf, int len, int timeout) {
    int ret = 0;
    size_t rsum = 0;
    fd_set rset;
    struct timeval time;
    char *readBuf = buf;
    int readLen = len;

    while (rsum < readLen) {
        time.tv_sec = timeout / 1000;
        time.tv_usec = (timeout % 1000) * 1000;
        FD_ZERO(&rset);
        FD_SET(fd, &rset);
        ret = select(fd + 1, &rset, NULL, NULL, &time);
        if (ret < 0) { // 信号中断
            printf("select error!\r\n");
            return -1;
        } else if (ret == 0) { // 超时
            // printf("time over!\r\n");
            return rsum;
        } else {
            ret = read(fd, readBuf + rsum, readLen - rsum);
            if (ret < 0) {
                printf("read data failed\r\n");
                return ret;
            } else {
                rsum += ret;
            }
        }
    }

    return rsum;
}

/*
 * @berf uart 发送数据
 * @param int fd: 串口文件描述符
 * @param void *buf: 发送数据buffer
 * @param int len: 数据缓冲长度
 *
 * @berf uart send
 * @param int fd: uart file descriptor
 * @param void *buf: send data buf
 * @param int len: data buf len
 */
int uart_write(int fd, const char *buf, int len) {
    int ret = 0;
    int count = 0;
    const char *sendBuf = buf;
    int sendLen = len;

    tcflush(fd, TCIFLUSH);

    while (sendLen > 0) {
        ret = write(fd, sendBuf + count, sendLen);
        if (ret < 1) {
            printf("write data below 1 byte % d\r\n", ret);
            break;
        }
        count += ret;
        sendLen = sendLen - ret;
    }

    return count;
}
